"use strict"
// 基础服务组件库 by clonne/2021
import "./ramda.js"

// 时钟/时间相关服务
// 全局定时器：以固定帧率定时运行，每帧通知帧观察者
// 同步刷新：根据设备刷新率回调，其实就是requestAnimationFrame
const clock = (()=>{
    let _gap_ms = undefined
    const _frame_watchs = []
    function frame(){
        if (_gap_ms > 0){
            _frame_watchs.forEach((_)=>_())
            setTimeout(frame, _gap_ms)
        }
    }
    const me = {}
    me.frame_watch = (f)=>{
        if (f instanceof Function){
            _frame_watchs.push(f)
        }
        return me
    }
    me.start = (fps)=>{
        _gap_ms = R.max(1000/fps, 1)
        setTimeout(frame, 0)
        return me
    }
    me.stop = ()=>{
        _gap_ms = 0
        return me
    }
    me.now_ms = ()=>{
        return Date.now()
    }
    me.now_second = ()=>{
        return Math.floor(Date.now() / 1000)
    }
    me.sync_refresh = (f)=>{
        function loop(){
            if (f() == undefined){
                window.requestAnimationFrame(loop)
            }
        }
        return window.requestAnimationFrame(loop)
    }
    return me
})()

// 本地键值存储系统(String Based)
const redis = (()=>{
    const cache = {}
    function cache_get(k){
        const v = cache[k]
        if (R.isNil(v)){
            cache[k] = window.localStorage.getItem(k)
        }
        return cache[k]
    }
    function cache_set(k, v){
        if (cache[k] != v){
            window.localStorage.setItem(k, v)
            cache[k] = v
        }
        return v
    }
    const me = {}
    me.Event = {
        // 在系统重建时触发(应用第一次运行或关键信息损害，此时清除数据)
        on_reset:R.always(true),
        // 在键不正确时触发(读或写)：on_key_error(k)
        // 对于get/set，返回此回调的返回值
        on_key_error:R.always(""),
        // 在键值不存在时触发：user_v = on_key_notfound(k)
        // 若user_v为null或undefined，立即返回undefined
        // 否则将user_v写入键值并返回
        on_key_notfound:R.always(null),
    }
    function kv_set(k, v){
        if (k.length > 0){
            const struct = R.isNil(v) ? `${v}` : v
            return cache_set(k, struct)
        }
        return me.Event.on_key_error(k)
    }
    function kv_get(k){
        if (k.length > 0){
            let struct = cache_get(k)
            // 不存在此键值？ 通知用户赶紧的
            if (R.isNil(struct)){
                const user_v = me.Event.on_key_notfound(k)
                if (R.isNil(user_v)){return undefined}
                if (R.is(String, user_v)){return (me.text[k] = user_v)}
                if (R.is(Number, user_v)){return (me.number[k] = user_v)}
                if (R.is(Boolean, user_v)){return (me.bool[k] = user_v)}
                return (me.text[k] = user_v.toString())
            }
            return struct
        }
        return me.Event.on_key_error(k)
    }

    let _last_update_time = undefined
    function fix_user_key(k){
        if (k.length > 0){
            if (k[0] == '~'){return `_u_${k}`}
        }
        return k
    }
    me.text = new Proxy({}, {
        get(_, k){
            return kv_get(fix_user_key(k))
        },
        set(_, k, v){
            if (R.is(String, v)){
                return kv_set(fix_user_key(k), v)
            }else{
                return kv_set(fix_user_key(k), v.toString())
            }
        }
    })
    me.number = new Proxy({}, {
        get(_, k){
            const a = kv_get(fix_user_key(k))
            return (R.isNil(a)) ? NaN : Number(a)
        },
        set(_, k, v){
            return kv_set(fix_user_key(k), v.toString())
        },
    })
    me.bool = new Proxy({}, {
        get(_, k){
            const a = kv_get(fix_user_key(k))
            return ("1" == a) || (Boolean(a))
        },
        set(_, k, v){
            return kv_set(fix_user_key(k), (Boolean(v) ? "1" : "0"))
        },
    })
    const ARRAY_SEQ = ','
    me.array = new Proxy({}, {
        get(_, k){
            const a = kv_get(fix_user_key(k))
            const b = R.is(String,a) ? a.split(ARRAY_SEQ) : []
            const c = b.map((x)=>(x.replace(`\\${ARRAY_SEQ}`, ARRAY_SEQ)))
            return c
        },
        set(_, k, v){
            const a = v.map((x)=>(String(x).replace(ARRAY_SEQ, `\\${ARRAY_SEQ}`)))
            const b = a.join(ARRAY_SEQ)
            return kv_set(fix_user_key(k), b)
        },
    })
    me.clear = ()=>{
        console.log("[LIB|UTILS] clear storage are not allowed")
        return me
    }
    me.sets = (val_map)=>{
        for (const k in val_map) {
            me.text[k] = val_map[k]
        }
        return me
    }
    me.init = (eventp=undefined)=>{
        if (! R.isNil(eventp)) {
            for (const m in me.Event) {
                if (eventp.hasOwnProperty(m)) {
                    me.Event[m] = eventp[m]
                }
            }
        }
        const fuck = window.localStorage.getItem("~Redis.fuck")
        if (R.isNil(fuck)){
            window.localStorage.clear()
            _last_update_time = Date.now()
            window.localStorage.setItem("~Redis.fuck", _last_update_time.toString())
            me.Event.on_reset()
        }else{
            _last_update_time = Number(fuck)
        }
    }
    me.DEFAULT_KVS = (prefix, dict)=>{
        const fixmap = {}
        for (const k in dict) {
            const fix_k = `[${prefix}]${k}`
            fixmap[fix_k] = dict[k]
        }
        return fixmap
    }
    me.make_kvs = (prefix)=>{
        function _f(t, k, v=undefined) {
            const k_fix = `[${prefix}]${k}`
            if (R.isNil(v)) {
                return t[k_fix]
            } else {
                t[k_fix] = v
            }
        }
        return {
            text(k, v=undefined){return _f(me.text, k, v)},
            number(k, v=undefined){return _f(me.number, k, v)},
            bool(k, v=undefined){return _f(me.bool, k, v)},
        }
    }
    return me
})()

export default {
    clock,
    redis,
}
